package com.zte.claa.inficombo.csa.app.service.rptparse.ext;


import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;

import com.zte.claa.inficombo.csa.app.comm.csif.CSIFCommService;
import com.zte.claa.inficombo.csa.app.config.LocOptConfProps;
import com.zte.claa.inficombo.csa.app.model.dev.ClaaDevInitState;
import com.zte.claa.inficombo.csa.app.model.dev.DevBase;
import com.zte.claa.inficombo.csa.app.model.dev.DevTypeModel;
import com.zte.claa.inficombo.csa.app.model.dev.LocLoRaBeacon;
import com.zte.claa.inficombo.csa.app.model.redis.DevMeteData;
import com.zte.claa.inficombo.csa.app.model.redis.LocMessageBean;
import com.zte.claa.inficombo.csa.app.service.custparam.LocCustParamModelService;
import com.zte.claa.inficombo.csa.app.service.custparam.impl.CoordinateConverter;
import com.zte.claa.inficombo.csa.app.service.custparam.impl.CoordinateInfo;
import com.zte.claa.inficombo.csa.app.service.redis.CSAMessagePublisher;
import com.zte.claa.inficombo.csa.app.service.rptparse.LocRawData;
import com.zte.claa.inficombo.csa.app.service.rptparse.ParserContainer;
import com.zte.claa.inficombo.csa.app.service.rptparse.ParserUpdata;
import com.zte.claa.inficombo.csa.app.util.JsonUtils;


/**
 * 		自研lora定位设备：
		{"cmd":"updata","deveui":"4a770065150006","payload":"IGwKFicMBQAKZXEAEQAKZUsGUgFjUQAAAAA","port":34,
		 "detail":{"app":{"moteeui":"4a770065150006","appeui":"2c26c50065100001","dir":"up","seqno":47637,
          		          "userdata":{"port":34,"payload":"FmSZOjRHXSsC6c7FXCcSCP/9yYDXxlH6Lxg","conf":false},
		  				  "motetx":{"freq":485.5,"datr":"SF7BW125","codr":"4/5","adr":false},
		  				  "gwrx":[{"gweui":"6073bc0001100725","ant":1,"ChMask":"00:00:0f:f0:00:00","time":"2018-07-20T03:00:52.612039Z","timefromgateway":true,"chan":6,"rfch":1,"rssic":-105,"lsnr":5.7},
		          				  {"gweui":"6073bc0001100722","ant":1,"ChMask":"00:00:0f:f0:00:00","time":"2018-07-20T03:00:52.612040Z","timefromgateway":true,"chan":6,"rfch":1,"rssic":-100,"lsnr":-2},
				  				  {"gweui":"6073bc0001100724","ant":1,"ChMask":"00:00:0f:f0:00:00","time":"2018-07-20T03:00:52.607Z","timefromgateway":false,"chan":6,"rfch":1,"rssic":-113,"lsnr":1.2}
				 		   ]
				 		  }
				   }
		}
 *
 */

public class ClaaLocUpdataSubParser extends ParserUpdata implements UpDataSubParser {
	public static final String PARSER_TP_CLAALOC = "sp_claa_loc_parser";
	
	public SimpleDateFormat datetimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	
	// 日志记录.
	private Logger logger = LoggerFactory.getLogger(ClaaLocUpdataSubParser.class);
	
	// 配置参数
	private LocOptConfProps configProp = null;
	
	// 消息主题
	private int beaconPubIndex = 0;
	private CSAMessagePublisher[] beaconPublisherArr = null;
	
	private int anchorPubINdex = 0;
	private CSAMessagePublisher[] anchorPublisherArr = null;
	
	//gw的lora信号强度rssi发布器
	private CSAMessagePublisher gwLoraRssiPublisher = null;
	
	//gps坐标数据处理队列
	private LinkedBlockingQueue<List<DevMeteData>> queue = null;	
		
	private boolean isWorkOn = true;
	
	//原始gps数据队列
	private LinkedBlockingQueue<List<DevMeteData>> originalGpsQueue = null;	
	
	// MEMS解析字段队列.
	private LinkedBlockingQueue<List<DevMeteData>> devMemsDataQueue = null;	
		
	
	// 主框架会在初始化时调用该方法.
	public void setParserContainer(ParserContainer container){
		super.setParserContainer(container);
		this.initLocRawDataPublishers();		 
		this.initGpsCorrection();
		this.initOriginalGpsSaveDB();
		this.initDevMemsDataSaver();
		this.initDevPowerProcessTask();	
	}
	
		
	private void initLocRawDataPublishers(){
		try{
			this.configProp = this.container.getLocOptConfigure();
			StringRedisTemplate redisStrTemplate = this.container.getRedisStrTemplate();
			int beaconPubNum = this.configProp.getBeacontopicnum();
			if(beaconPubNum > 0){
				String beaconTopicPrefix = this.configProp.getBeacontopicprefix().trim();
				this.beaconPubIndex = 0; 
				this.beaconPublisherArr = new CSAMessagePublisher[beaconPubNum];
				for(int b=0; b<beaconPubNum; b++){
					this.beaconPublisherArr[b] = new CSAMessagePublisher(redisStrTemplate, beaconTopicPrefix+b);
				}
			}
			int anchorPubNum = this.configProp.getAnchortopicnum();
			if(anchorPubNum > 0){
				String anchorTopicPrefix = this.configProp.getAnchortopicprefix().trim();
				this.anchorPubINdex = 0; 
				this.anchorPublisherArr = new CSAMessagePublisher[anchorPubNum];
				for(int a=0; a<anchorPubNum; a++){
					this.anchorPublisherArr[a] = new CSAMessagePublisher(redisStrTemplate, anchorTopicPrefix+a);
				}
			}
			logger.info("initLocRawDataPublishers ok: beaconPubNum={}, anchorPubNum={}.", beaconPubNum, anchorPubNum);
			
			//构造gw的lora信号发送器
			this.gwLoraRssiPublisher = new CSAMessagePublisher(redisStrTemplate,this.container.getRedisChannelProps().getGwlorarssichannel());
			
		}catch(Exception e){
			logger.error("initLocRawDataPublishers failed!", e);
		}
	}

	
	
	//gps坐标优化任务线程
	private void initGpsCorrection(){		
		this.queue = new LinkedBlockingQueue<List<DevMeteData>>(10000);	
		this.isWorkOn = true;
		Thread gpsCorrectionThread = new Thread(new Runnable() {
			@Override
			public void run() {
				launchGpsCorrection();
			}
		});
		gpsCorrectionThread.setName("gpsCorrectionThread");
		gpsCorrectionThread.start();	
		logger.info("gpsCorrection thread started: gpsCorrectionThread");
	} 

	
	//gps纠偏任务
	private void launchGpsCorrection(){
		List<DevMeteData> dataList = null;		
		
		while(this.isWorkOn){			
			try{								
				dataList = this.queue.poll(20, TimeUnit.SECONDS);
				
				if(dataList == null){
					continue;
				} 				
				//gps纠偏
				this.container.getLocCorrectionService().correctGps(dataList);
							
				if(dataList.size() != 0){			
					//纠偏完成后,调用重载的saveClaaLocRawData
					this.saveClaaLocRawData(dataList, LocRawData.GSP_DATA_TABLE);
				}						
			}catch (Throwable t) {
				logger.error("LocCoordRecordSave failed: " + t.getMessage(), t);
			}			
		}
		logger.warn("LocCoordRecordSaver thread exit: " + Thread.currentThread().getName());
	}

	
	
	//gps原始坐标入库任务线程
	private void initOriginalGpsSaveDB(){		
		this.originalGpsQueue = new LinkedBlockingQueue<List<DevMeteData>>(5000);	
		if(this.configProp.isSaveoriginalgps()){			
			Thread originalGpsSaveThread = new Thread(new Runnable(){
				@Override
				public void run() {
					launchOriginalGpsSaveDB();
				}
			});			
			originalGpsSaveThread.setName("originalGpsSaveThread");
			originalGpsSaveThread.start();	
			logger.info("originalGpsSavethread started: originalGpsSaveThread");
		}		
	}
	
	
	
	//初始化mems数据入库线程.
	private void initDevMemsDataSaver(){	
		if(this.configProp.isSavememsstate()){	
			this.devMemsDataQueue = new LinkedBlockingQueue<List<DevMeteData>>(5000);	
			Thread memsSaveThread = new Thread(new Runnable(){
				@Override
				public void run() {
					launchDevMemsSave2DB();
				}
			});
			memsSaveThread.setName("LocDevMemsDataSaver");
			memsSaveThread.start();	
			logger.info("initDevMemsDataSaver started: LocDevMemsDataSaver");
		} else {
			this.devMemsDataQueue = new LinkedBlockingQueue<List<DevMeteData>>(1);	
		}
	}
		
	
	
	//原始数据入库任务
	private void launchOriginalGpsSaveDB(){
		List<DevMeteData> dataList = null;
		boolean isBatchSaveOk = true;		
		while(true){
			
			try{
				dataList = this.originalGpsQueue.poll(20, TimeUnit.SECONDS);
			}catch (Exception e) {
				logger.error("[0]saveOriginalGps failed:get data from originalGPSQueue error");
			}
							
			if(dataList != null && dataList.size() > 0){						
				try{						
					this.container.getPersisService().saveLocRawDataByBatch(dataList, LocRawData.GSP_DATA_TABLE_TEST);
					isBatchSaveOk = true;
				}catch(Exception e){
					isBatchSaveOk = false;
					logger.error("[1]saveOriginalGps failed: table=" + LocRawData.GSP_DATA_TABLE_TEST, e);
				}
				
				if(isBatchSaveOk == false){																							
					for(DevMeteData meteData : dataList){	
						try{// 防止批量保存时个别异常导致全部入库异常.
							this.container.getPersisService().saveLocRawData(meteData,  LocRawData.GSP_DATA_TABLE_TEST);
						}catch(Exception ex){
							logger.error("[2]saveOriginalGps failed: " +  LocRawData.GSP_DATA_TABLE_TEST + ", " + meteData, ex);
						}
					}						
				}									
			}											
		}
	}
	
			
	
	
	// mems数据入库...
	private void launchDevMemsSave2DB(){
		List<DevMeteData> dataList = null;
		boolean isBatchSaveOk = true;
		while(true){
			try{
				dataList = this.devMemsDataQueue.poll(20, TimeUnit.SECONDS);
			}catch (Throwable t) {
				logger.error("[0]save mems state failed failed: get data from queue error.");
			}	
			if(dataList != null && dataList.size() != 0){						
				try{						
					this.container.getPersisService().saveLocRawDataByBatch(dataList, LocRawData.MEMS_STATE_DATA_TABLE);
					isBatchSaveOk = true;
				}catch(Exception e){
					isBatchSaveOk = false;
					logger.error("[1]save mems state failed: table=" + LocRawData.MEMS_STATE_DATA_TABLE, e);
				}					
				if(isBatchSaveOk == false){																							
					for(DevMeteData meteData : dataList){	
						try{// 防止批量保存时个别异常导致全部入库异常.
							this.container.getPersisService().saveLocRawData(meteData,  LocRawData.MEMS_STATE_DATA_TABLE);
						}catch(Exception ex){
							logger.error("[2]save mems state failed: " +  LocRawData.MEMS_STATE_DATA_TABLE + ", " + meteData, ex);
						}
					}						
				}									
			}											
		}
	}

	
	
	@Override
	public String getParserType() {
		return PARSER_TP_CLAALOC;
	}

	
	
	@Override
	public void parseUpData(JSONObject json, DevBase dev, CSIFCommService svr) throws Exception {
		this.parseUpRptData(json, dev, svr);
	} 

	
	
	
	/**
	 * 克拉自研定位设备的解析.
	 * @param locData
	 */
	protected void parseClaaLocRawData(LocRawData locData, final DevBase dev) {
		// 将LocRawData的jsondata列表转换为 metedata列表，然后持久化. 
		logger.info("Begin process claalocrawdata: "+locData);
		try{
			List<String> datas = locData.getGpsJsonDatas();
			if(datas != null && datas.size() > 0){
				this.saveClaaLocRawData(datas, dev, LocRawData.GSP_DATA_TABLE);
			}
		}catch(Exception e){
			logger.error("[parseClaaLocRawData].gps failed.", e);
		}
		try{
			List<String> datas = locData.getLoraTerminalJsonDatas();
			if(datas != null && datas.size() > 0){
				this.saveClaaLocRawData(datas, dev, LocRawData.LORA_TERMINAL_DATA_TABLE);
			}
		}catch(Exception e){
			logger.error("[parseClaaLocRawData].lora failed.", e);
		}
		try{
			List<String> datas = locData.getAnchorJsonDatas();
			if(datas != null && datas.size() > 0){
				// 尝试发布...
				this.publishAnchorLocRawData(datas, dev);
				// 判断是否需要存库...
				if(this.configProp == null || (this.configProp.isSavetodb() == true)){
					this.saveClaaLocRawData(datas, dev, LocRawData.BT_ANCHOR_DATA_TABLE);
				}
			}
		}catch(Exception e){
			logger.error("[parseClaaLocRawData].bt.anchor failed.", e);
		}
		try{
			List<String> datas = locData.getBeaconJsonDatas();
			if(datas != null && datas.size() > 0){
				// 尝试发布...
				this.publishBeaconLocRawData(datas, dev);
				// 判断是否存库..
				if(this.configProp == null || (this.configProp.isSavetodb() == true)){
					this.saveClaaLocRawData(datas, dev, LocRawData.BT_BEACON_DATA_TABLE);
				}
			}
		}catch(Exception e){
			logger.error("[parseClaaLocRawData].bt.beacon failed.", e);
		}
		try{
			List<String> datas = locData.getGpsDetailJsonData();
			if(datas != null && datas.size() > 0){
				this.saveClaaLocRawData(datas, dev, LocRawData.GPS_DETAIL_TABLE);
			}
		}catch(Exception e){
			logger.error("[parseClaaLocRawData].gps.detail failed.", e);
		}
		try{
			List<String> datas = locData.getLoraBeaconData();
			if (datas != null && datas.size() > 0){
				this.saveClaaLocRawData(datas, dev, LocRawData.GSP_DATA_TABLE);
				this.updateDevStatus(datas, dev);//lora信标坐标需要更新到配置表中
			}
		}catch(Exception e){
			logger.error("[parseClaaLocRawData].lora.beacon failed.", e);
		}
		// MEMS字段解析数据处理》	
		try{
			logger.info("Begin process lcs_dev_mems_state: "+locData);
			List<String> datas = locData.getMemsStateJsonData();
			if(datas != null && datas.size() > 0){
				this.parseAndSaveLocMemsStateData(datas, dev);
			}
		}catch(Exception e){
			logger.error("[parseClaaLocRawData].gps failed.", e);
		}
		//发布gw的lora信号轻度rssi		
		try{
			logger.info("Begin process gw lora rssi: "+locData);
			//如果开关发布开关打开就执行发布
			if(this.container.getLocCorrectionService().getGwPublishStatus()){
				List<String> datas = locData.getLoraRssiData();
				if(datas != null && datas.size() > 0){
					// 尝试发布...
					this.publishGwLoraRssiData(datas, dev);				
				}
			}			
		}catch(Exception e){
			logger.error("[parseClaaLocRawData].gw lora rssi failed.", e);
		}
		//解析终端电量数据
		try{
			List<String> datas = locData.getDevPowerData();
			if(datas != null && datas.size() > 0){
				this.pushDevPowerData2Queue(datas, dev);
			}
		}catch(Exception e){
			logger.error("[parseClaaLocRawData].device power data failed.", e);
		}
		/*解析蓝牙信标数据(信标是随终端一起报上来,js插件先将数据包装)
		 * locData.getLabelPowerData() 获取的datas中包含若干
		 * btlabel的电压等级,需要每个单独处理
		 */
		try{
			List<String> datas = locData.getLabelPowerData();			
			if(datas != null && datas.size() > 0){
				for(String data : datas){
					this.processBeaconVoltLevel(data, dev);
				}				
			}
		}catch(Exception e){
			logger.error("[parseClaaLocRawData].bt beacon volt level failed.", e);
		}
		//解析终端与网管信号状态数据
		try{
			List<String> datas = locData.getDeveuiGwStateData();
			if(datas != null && datas.size() > 0){
				this.saveClaaLocRawData(datas, dev, LocRawData.DEVICE_GW_STATE_TABLE);
			}
		}catch(Exception e){
			logger.error("[parseClaaLocRawData].device gw state data failed.", e);
		}
		
		//解析uwb定位数据		
		try{
			logger.info("Begin process uwb beacon data: "+locData);
			List<String> datas = locData.getUwbJsonData();
			if(datas != null && datas.size() > 0){
				// 这里直接使用蓝牙定位的主题发布
				this.publishBeaconLocRawData(datas, dev);
			}
		}catch(Exception e){
			logger.error("[parseClaaLocRawData].uwbbeacon failed.", e);
		}
		
		// 检查是否是为自研设备的初始化状态数据上报.
		if(locData.hasActivedData()){
			this.saveClaaDevInitState(locData, dev);
		}		
	}
	
		
	/**
	 * 更新lora信标的坐标:正常流程需要缓存与db做对比后判断是否需要更新到db,
	 * 但是lora信标即使安装固定,每次上报的gps都会存在差异,这里使用缓存对比更新意义不大
	 * 而且信标的上报周期一般都是以小时为单位,没有数据库压力,所以这里直接更新db
	 * 
	 * @param jsonDatas
	 * @param dev
	 */
	private void updateDevStatus(List<String> jsonDatas , final DevBase dev){		
		if(jsonDatas == null || jsonDatas.size() == 0) 
			return;
		
		//jsplugin解析的lora信标数据List只有一个
		String rptData = jsonDatas.get(0);
		Map<String, Object> mdata = JsonUtils.jsonStrToHashMap(rptData.toString());
	
		String locX = this.getMapValue(mdata, "lat", "254");
		String locY = this.getMapValue(mdata, "lon", "254");	
		if("254".equals(locX) || "254".equals(locY))//定位失败不更新
			return;
		
		LocLoRaBeacon locLoRaBeacon = new LocLoRaBeacon();		
		locLoRaBeacon.setLocX(Double.parseDouble(locX));
		locLoRaBeacon.setLocY(Double.parseDouble(locY));
		locLoRaBeacon.setDeveui(dev.getDevEui());
		locLoRaBeacon.setProjectId(dev.getProjectId());
					
		try{
			this.container.getLocLoRaBeaconMapper().updateCoordinatePrimaryKey(locLoRaBeacon);
		}catch (Exception e) {
			logger.error("[updateDevStatus].lora.beaconInfo failed.", e);
		}
		try{
			this.container.getLocLoRaBeaconMapper().updateProjectDevCoordinate(locLoRaBeacon);
		}catch (Exception e) {
			logger.error("[updateDevStatus].lora.beaconProject failed.", e);
		}		
	}

	
	
	
	// 保存或更新设备初始化状态.
	private void saveClaaDevInitState(LocRawData locData, final DevBase dev) {
		try{
			ClaaDevInitState state = new ClaaDevInitState();
			state.setConfirmactive(locData.getActived());
			state.setDeveui(dev.getDevEui());
			state.setDevtype(dev.getDevType());
			state.setProjectid(dev.getProjectId());
			this.container.getDevConfDataSenderService().submitClaaDevInitState(state);
			logger.info("SubmitClaaDevInitState ok: "+state); 
		}catch(Exception e){
			logger.error("saveClaaDevInitState failed: "+dev.getDevEui()+", state="+locData.getActived(), e);
		}		
	}
	
	
	
	//构造metedata
	private DevMeteData buildDevMeteData(Map<String, Object> mdata, final DevBase dev){
		if(mdata.isEmpty()){
			return null;
		}		
		DevMeteData meteData = new DevMeteData();
		meteData.setAppeui(dev.getAppEui());
		meteData.setDeveui(dev.getDevEui());
		meteData.setDevname(dev.getDevName());
		meteData.setDevtype(dev.getDevType());
		meteData.setPort("0");
		meteData.setProjectid(dev.getProjectId());
		meteData.setMdata(mdata);		
		return meteData;
	}                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
	
	
	
	
	//保存转换后的gps坐标数据
	private void saveConversionGps(List<String> jsonDatas, final DevBase dev){
		List<DevMeteData> dataList = new ArrayList<DevMeteData>();
		
		for (String rptData : jsonDatas) {
			Map<String, Object> mdata = JsonUtils.jsonStrToHashMap(rptData.toString());				
			dataList.add(buildDevMeteData(converterCoordinate(mdata,dev), dev));			
		}			
		//需要gps->xyz的坐标,转换后入队列,交给坐标纠偏服务处理
		boolean ret = this.queue.offer(dataList);
		if(!ret){
			logger.warn("add devGpsData to queue failed: " + dataList.get(0).getDeveui());
		}		
	}
		
	
	
	//保存转换前的原始gps坐标数据
	private void saveOriginalGps(List<String> jsonDatas, final DevBase dev){
		List<DevMeteData> orgDataList = new ArrayList<DevMeteData>();
		
		for (String rptData : jsonDatas) {
			Map<String, Object> temp = JsonUtils.jsonStrToHashMap(rptData.toString());
			Map<String, Object> orgMdata = new HashMap<String, Object>();
			orgMdata.put("projectid", temp.get("projectid"));
			orgMdata.put("in_out_flag", temp.get("in_out_flag"));
			orgMdata.put("type", temp.get("type"));
			orgMdata.put("lon", temp.get("lon"));
			orgMdata.put("lat", temp.get("lat"));
			orgMdata.put("alt", temp.get("alt"));
			orgMdata.put("cur_volt",temp.get("cur_volt"));					
			orgDataList.add(buildDevMeteData(orgMdata,dev));
		}			
		//将原始gps坐标存入队列
		if(this.configProp.isSaveoriginalgps() && orgDataList != null && orgDataList.size() != 0){
			boolean ret = this.originalGpsQueue.offer(orgDataList);
			if(!ret){
				logger.warn("add originalGpsData to queue failed: " + orgDataList.get(0).getDeveui());
			}
		}	
	}
		
	
   /**
	 * 克拉自研定位设备的解析.
	 * @param locData
	 */
	private void saveClaaLocRawData(List<String> jsonDatas, final DevBase dev, String tabName) {
		 //需要转换且是gps坐标需要直接入gps表
		if(LocRawData.GSP_DATA_TABLE.equalsIgnoreCase(tabName.trim()) && needConverte(dev)){
			//保存转换后的坐标
			saveConversionGps(jsonDatas, dev);			
			//如果开关打开,则保存转换前的gps数据
			if(this.configProp.isSaveoriginalgps()){
				saveOriginalGps(jsonDatas, dev);
			}			
		}else{
			List<DevMeteData> dataList = new ArrayList<DevMeteData>();
			for (String rptData : jsonDatas) {
				Map<String, Object> mdata = JsonUtils.jsonStrToHashMap(rptData.toString());			
				dataList.add(buildDevMeteData(mdata,dev));
			}
			saveClaaLocRawData(dataList,tabName);
		}
	}

	
	
	/**
	 * 重载saveClaaLocRawData,方便gps纠偏时坐标的异步操作
	 */
	private void saveClaaLocRawData(List<DevMeteData> dataList, String tabName) {
		boolean isBatchSaveOk = true;
		// 自研定位设备类型.入库到基础库的lcs相关表中...
		if(!LocRawData.GSP_DATA_TABLE.equalsIgnoreCase(tabName.trim())){
			try{				
				this.container.getPersisService().saveLocRawDataByBatch(dataList, tabName);
				isBatchSaveOk = true;
			}catch(Exception e){
				isBatchSaveOk = false;
				logger.error("[1]saveClaaLocRawData failed: table="+tabName, e);
			}
			// 防止批量保存时个别异常导致全部入库异常.
			if(isBatchSaveOk == false){
				for(DevMeteData meteData : dataList){
					try{								
						this.container.getPersisService().saveLocRawData(meteData, tabName);
						logger.info("[*^_^*]Save claa loc Rawdata message ok: " + JsonUtils.toJson(meteData));
					}catch(Exception ex){				
						logger.error("[2]saveClaaLocRawData failed: " + tabName + ", " + meteData, ex);
						continue;
					}				
				}
			}							
		}else{//gps坐标数据统一在base侧入库,csa不入库			
			for(DevMeteData meteData : dataList){
				// LCS的GPS坐标数据需要广播，其他的原始定位数据不需要广播...
				LocMessageBean locMB = this.transformGpsMeteData(meteData);
				String pubMsg = JsonUtils.toJson(locMB);
				this.container.getClaaLocGpsCoordPublisher().publishMessage(pubMsg);
				logger.info("[*^_^*]Publish claa loc gps coordinate data ok: " + pubMsg);
				// 2020-04-29 配合GPS定位终端静止时参照历史定位结果构造最新一次的定位数据。
				if(locMB.getIn_out_flag() != LocMessageBean.FLAG_INVALID){
					this.container.getPersisService().putMeteDataToCache(meteData);
				}
			}			
		}					
	}
	
		
	/**
	 * 克拉自研定位设备的MEMS状态数据解析.
	 * @param locData
	 */
	private void parseAndSaveLocMemsStateData(List<String> jsonDatas, final DevBase dev) {
		if(jsonDatas == null || this.configProp.isSavememsstate() == false){
			return;
		}
		// 将LocRawData的jsondata列表转换为 metedata列表，然后持久化. 
		List<DevMeteData> dataList = new ArrayList<DevMeteData>();	
		for (String rptData : jsonDatas) {
			Map<String, Object> mdata = JsonUtils.jsonStrToHashMap(rptData.toString());	
			if(mdata != null){
				mdata.put("projectid", dev.getProjectId());
				mdata.put("devname", String.valueOf(dev.getDevName()));
				mdata.put("collecttime", new Timestamp(System.currentTimeMillis()));
			}
			dataList.add(buildDevMeteData(mdata,dev));
		}
		//将MEMS-state数据存入队列
		if(this.configProp.isSavememsstate() && dataList != null && dataList.size() != 0){
			boolean b = this.devMemsDataQueue.offer(dataList);
			if(!b){
				logger.warn("add locdevmemsstateData to queue failed: " + dataList.get(0).getDeveui());
			}
		}	
	}
	
	
	
	//判断是否需要转换
	private Boolean needConverte(final DevBase dev){
		String projectid = dev.getProjectId();
		if(projectid == null || projectid.length() == 0) {
			return false;		
		}
		CoordinateConverter converter = LocCustParamModelService.locCustParamMap.get(projectid);
		//转换器不存在
		if (converter == null){
			logger.info("gps<-->xyz[needConverte] is false");
			return false;			
		}else{
			return true;
		}
	}

	
	
	//坐标转换
	private Map<String, Object> converterCoordinate(Map<String, Object> mdata,final DevBase dev){		
		//获取坐标转换器
		try{
			CoordinateConverter converter = LocCustParamModelService.locCustParamMap.get(dev.getProjectId());
			if (converter == null){
				logger.warn("the coverter is null projectid:{}",dev.getProjectId());
				return mdata;
			}
							
			String strLon = (String)mdata.get("lon");
			String strLat = (String)mdata.get("lat");
			
			if(strLon == null || strLon.length() == 0 || strLat == null || strLat.length() == 0){
				return mdata;
			}
													
			if (strLon.equals("254") || strLat.equals("254")){//254是无效定位数据				
				return mdata;
			}
			
			logger.info("before converte Coordinate -> dev:{}, lon:{}, lat:{} ",dev.getDevEui(),strLon,strLat);
			
			CoordinateInfo point = new CoordinateInfo();
			point.setLon(Double.parseDouble(strLon));
			point.setLat(Double.parseDouble(strLat));			
			//地图角度可能存在偏移,矫正下
			converter.converterPoint(point);
			
			logger.info("after converte Coordinate -> dev:{}, x:{}, y:{} ",dev.getDevEui(),point.getxLong(),point.getyLong());
						
			mdata.put("lon", Long.toString(point.getxLong()));
			mdata.put("lat", Long.toString(point.getyLong()));
			mdata.put("zone_id", Integer.toString(converter.getZoneid()));
			return mdata;
		}catch (Exception e) {
			logger.info("gps<-->xyz[converterCoordinate] fail : " + e.getMessage());
			return mdata;
		}		
	}
	
	
	
	/**
	 * 将定位终端直接上报的GPS坐标信息（metedata）转换为LocMessageBean
	 *
	 * @param meteData
	 * @return
	 */
	private LocMessageBean transformGpsMeteData(final DevMeteData meteData){
		LocMessageBean locMB = new LocMessageBean();
		locMB.setDeveui(meteData.getDeveui());
		locMB.setProjectid(meteData.getProjectid());
		locMB.setReport_time(datetimeFormat.format(meteData.getRpttime()));
		Map<String, Object> mdataMap = meteData.getMdata();
		locMB.setAlt(Double.parseDouble(this.getMapValue(mdataMap, "alt", "0")));
		locMB.setIn_out_flag(Byte.parseByte(this.getMapValue(mdataMap, "in_out_flag", String.valueOf(LocMessageBean.FLAG_OUT))));
		locMB.setLat(Double.parseDouble(this.getMapValue(mdataMap, "lat", "0")));
		locMB.setLon(Double.parseDouble(this.getMapValue(mdataMap, "lon", "0")));
		locMB.setType(Byte.parseByte(this.getMapValue(mdataMap, "type", "34")));
		locMB.setZone_id(Integer.parseInt(this.getMapValue(mdataMap, "zone_id", "0")));
		locMB.setMemsShockFlag(Integer.parseInt(this.getMapValue(mdataMap, "mems_shock_flag", "0")));
		locMB.setxAngle(Float.parseFloat(this.getMapValue(mdataMap, "xAngle", "0")));
		locMB.setyAngle(Float.parseFloat(this.getMapValue(mdataMap, "yAngle", "0")));
		locMB.setzAngle(Float.parseFloat(this.getMapValue(mdataMap, "zAngle", "-90")));	
		
		locMB.setSos(Integer.parseInt(this.getMapValue(mdataMap, "sos", "0")));	
		locMB.setPress(Float.parseFloat(this.getMapValue(mdataMap, "press", "0")));	
		locMB.setTemp(Float.parseFloat(this.getMapValue(mdataMap, "temp", "0")));
		locMB.setHumd(Float.parseFloat(this.getMapValue(mdataMap, "humd", "0")));
		// 增加电压和电量字段
		locMB.setCur_percent(Float.parseFloat(this.getMapValue(mdataMap, "cur_percent", "95")));
		locMB.setCur_volt(Float.parseFloat(this.getMapValue(mdataMap, "cur_volt", "3.5")));
		
		return locMB;
	}
	
	
	
	/**
	 * 解析metedata的值
	 * 
	 * @param mdataMap
	 * @param keyName
	 * @param defVal
	 * @return
	 */
	private String getMapValue(Map<String, Object> mdataMap, String keyName, String defVal){
		try{
			Object obj = mdataMap.get(keyName);
			if(obj != null && !obj.toString().equals("")){
				return obj.toString();
			}
		}catch(Exception ignore){}
		return defVal;
	}
	
	
	
	/**
	 * 重载metedata解析结果处理方法（仅用于蓝牙终端+蓝牙信标定位模式）.
	 * 批量发布mete数据. 
	 * @param dev
	 * @param rptData
	 * @param devPort
	 * @throws Exception
	 */
	protected void parseMeteDataByBatch(DevBase dev, List<String> rptDataList, String devPort) throws Exception {
		String devTypeId = dev.getDevType();
		DevTypeModel dtm = this.container.getCacheManager().getDevTypeModel(devTypeId);
		if(dtm == null || dtm.getDmList() == null){
			logger.error("[*!!*]Can not get datamodel for devtype="+devTypeId);
			return;
		}
		List<DevMeteData> dataList = new ArrayList<DevMeteData>();
		for (String rptData : rptDataList) {
			Map<String, Object> mdata = JsonUtils.jsonStrToHashMap(rptData.toString());
			dataList.add(buildDevMeteData(mdata,dev));
		}
		boolean isBatchSaveOk = true;
		// 自研定位设备类型.入库到基础库的lcs相关表中...
		try{
			this.container.getPersisService().saveLocRawDataByBatch(dataList, dtm.getBase().getTbname());
			isBatchSaveOk = true;
		}catch(Exception e){
			isBatchSaveOk = false;
			logger.error("saveLocRawDataByBatch failed: "+dtm.getBase().getTbname(), e);
		}
		for(DevMeteData meteData : dataList){
			try{
				// 防止批量保存时个别异常导致全部入库异常.
				if(isBatchSaveOk == false){
					this.container.getPersisService().saveLocRawData(meteData, dtm.getBase().getTbname());
				}
				// LCS的原始数据，不需要广播...
				String pubMsg = JsonUtils.toJson(meteData);
				// this.container.getClaaLocRawDataPublisher().publishMessage(pubMsg);
				logger.info("[*^_^*]Save claa loc rawdata message ok: "+pubMsg);
			}catch(Exception ex){
				logger.error("saveLocRawData failed: "+dtm.getBase().getTbname(), ex);
			}
		}
	}
	
	
	
	/**
	 * 重载父类方法（解析&发布mete数据）（仅用于蓝牙终端+蓝牙信标定位模式）
	 * 
	 * @param dev
	 * @param rptData
	 * @param devPort
	 * @throws Exception
	 */
	protected void parseMeteData(DevBase dev, String rptData, String devPort) throws Exception {
		String devTypeId = dev.getDevType();
		DevTypeModel dtm = this.container.getCacheManager().getDevTypeModel(devTypeId);
		if(dtm == null || dtm.getDmList() == null){
			logger.error("[*!!*]Can not get datamodel for devtype="+devTypeId);
			return;
		}
		Map<String, Object> mdata = JsonUtils.jsonStrToHashMap(rptData.toString());		
		DevMeteData meteData = buildDevMeteData(mdata,dev);	
		// 自研定位设备类型. 入库到基础库的lcs相关表中...
		this.container.getPersisService().saveLocRawData(meteData, dtm.getBase().getTbname());
		// LCS定位原始数据不需要构造广播数据，进行redis发布...
		String pubMsg = JsonUtils.toJson(meteData);
		// this.container.getClaaLocRawDataPublisher().publishMessage(pubMsg);
		logger.info("[*^_^*]Save claa loc rawdata message ok: "+pubMsg);
	}
	
	
	
	// 发布beacon的原始定位数据...
	private void publishBeaconLocRawData(List<String> jsonDatas, final DevBase dev){
		if(beaconPublisherArr == null || beaconPublisherArr.length == 0){
			logger.info("publishBeaconLocRawData: beaconPublisherArr is not init!");
			return;
		}
		for (String rptData : jsonDatas) {
			Map<String, Object> mdata = JsonUtils.jsonStrToHashMap(rptData.toString());
			// 选择发布器进行消息发布.
			String pubMsg = JsonUtils.toJson(buildDevMeteData(mdata,dev));
			int p = (this.beaconPubIndex++) % beaconPublisherArr.length;
			this.beaconPublisherArr[p].publishMessage(pubMsg);
			// 每100个复位一次...
			if(this.beaconPubIndex > 1000){
				this.beaconPubIndex = 0;
			}
			logger.info("[*^_^*]publishBeaconLocRawData ok: " + pubMsg);
		}
	}
	
	
	
	// 发布anchor的原始定位数据...
	private void publishAnchorLocRawData(List<String> jsonDatas, final DevBase dev){
		if(anchorPublisherArr == null || anchorPublisherArr.length == 0){
			logger.info("publishAnchorLocRawData: anchorPublisherArr is not init!");
			return;
		}
		for (String rptData : jsonDatas) {
			Map<String, Object> mdata = JsonUtils.jsonStrToHashMap(rptData.toString());
			// 选择发布器进行消息发布.
			String pubMsg = JsonUtils.toJson(buildDevMeteData(mdata,dev));
			int p = (this.anchorPubINdex++) % anchorPublisherArr.length;
			this.anchorPublisherArr[p].publishMessage(pubMsg);
			// 每100个复位一次...
			if(this.anchorPubINdex > 1000){
				this.anchorPubINdex = 0;
			}
			logger.info("[*^_^*]publishAnchorLocRawData ok: " + pubMsg);
		}
	}
	
	
	
	// 发布gw的lora 的rssi信号强度
	private void publishGwLoraRssiData(List<String> jsonDatas, final DevBase dev){
		if(gwLoraRssiPublisher == null){
			logger.info("publishGwLoraRssiData: gwLoraRssiPublisher is not init!");
			return;
		}
		for (String rptData : jsonDatas) {
			Map<String, Object> mdata = JsonUtils.jsonStrToHashMap(rptData.toString());

			// 选择发布器进行消息发布.
			String pubMsg = JsonUtils.toJson(buildDevMeteData(mdata,dev));
			this.gwLoraRssiPublisher.publishMessage(pubMsg);			
			logger.info("[*^_^*]publishGwLoraRssiData ok: " + pubMsg);
		}
	}
	
	
}
